package gbackup.main;

import gbackup.core.GBackupService;
import gbackup.core.RemoteDirectory;
import gbackup.core.RemoteFile;
import gbackup.core.Shell;
import gbackup.core.Utility;
import gbackup.error.CommandNotFoundException;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.URL;

import com.google.gdata.client.docs.DocsService;
import com.google.gdata.data.PlainTextConstruct;
import com.google.gdata.data.docs.DocumentListEntry;
import com.google.gdata.data.docs.FolderEntry;


public class PShell implements Shell {
	private static final String HOME = "GBackup";
	private final String PROMPT = " # ";

	private GBackupService _gBackupService = null;
	private DocsService _client = null;
	private InputStream _inputStream = null;
	private BufferedReader _inputBufferedReader = null;
	private OutputStream _outputStream = null;
	private PrintStream _outputPrintStream = null;
	private boolean _stop = false;
	private CommandDispatcher _commandDispatcher = null;
	private RemoteDirectory _remoteRootDirectory = null;
	private RemoteDirectory _remoteWorkingDirectory = null;
	private java.io.File _localRootDirectory = null;
	private java.io.File _localWorkingDirectory = null;

	public PShell(GBackupService gBackupService, DocsService client, InputStream intputStream, OutputStream outputStream) {
		this._gBackupService = gBackupService;
		this._client = client;
		
		this._inputStream = intputStream;
		this._inputBufferedReader = new BufferedReader(new InputStreamReader(this._inputStream));
		this._outputStream = outputStream;
		this._outputPrintStream = new PrintStream(this._outputStream, true);
		
		this._commandDispatcher = new CommandDispatcher();
	}

	private void cacheFileSystem() {
		this.checkRemoteHomeDirectory();
		this.addFilesRecursively(this._remoteRootDirectory);
		this._remoteWorkingDirectory = this._remoteRootDirectory;
	}

	private void addFilesRecursively(RemoteDirectory directory) {
		if (directory.getEntry().getResourceId() == null) {
			this._gBackupService.restartService();
			System.exit(0);
		}

		String allContentInFolder = "https://docs.google.com/feeds/default/private/full/" + directory.getEntry().getResourceId() + "/contents";

		for (DocumentListEntry entry : Utility.getDocumentListFeedByURL(this._client, allContentInFolder).getEntries()) {
			if (entry.getType().equals("folder")) {
				RemoteDirectory newDirectory = new RemoteDirectory(directory, entry);
				directory.addChild(newDirectory);
				this.addFilesRecursively(newDirectory);
			} else {
				RemoteFile newFile = new RemoteFile(directory, entry);
				directory.addChild(newFile);
			}
		}
	}

	private void checkRemoteHomeDirectory() {
		String allFolder = "https://docs.google.com/feeds/default/private/full/-/folder";
		for (DocumentListEntry entry : Utility.getDocumentListFeedByURL(this._client, allFolder).getEntries()) {
			if (entry.getTitle().getPlainText().equals(PShell.HOME)) {
				this._remoteRootDirectory = new RemoteDirectory(null, entry);
				break;
			}
		}
		if (this._remoteRootDirectory == null) {
			this._outputPrintStream.println("[Log] Create remote home directory [" + PShell.HOME + "]");

			DocumentListEntry newEntry = new FolderEntry();
			newEntry.setTitle(new PlainTextConstruct(PShell.HOME));
			try {
				URL feedUrl = new URL("https://docs.google.com/feeds/default/private/full/");
				this._client.insert(feedUrl, newEntry);
			} catch (Exception ex) {
				ex.printStackTrace();
				System.exit(-1);
			}
			this._remoteRootDirectory = new RemoteDirectory(null, newEntry);
		}
	}

	private void checkLocalHomeDirectory() {
		java.io.File localRootDirectory = new java.io.File(PShell.HOME);

		if (!localRootDirectory.exists()) {
			this._outputPrintStream.println("[Log] Create local home directory [" + PShell.HOME + "]");

			localRootDirectory.mkdir();
		}

		this._localRootDirectory = new java.io.File(PShell.HOME);
	}
	
	@Override
	public void start() {
		this.cacheFileSystem();

		this.checkLocalHomeDirectory();
		this._localWorkingDirectory = this._localRootDirectory;
		
		while (!this._stop) {
			String rawCommand = null;

			try {
				this._outputPrintStream.print(this._remoteWorkingDirectory.getCanonicalName() + this.PROMPT);
				rawCommand = this._inputBufferedReader.readLine().trim();
			} catch (IOException e) {
				e.printStackTrace();
				break;
			}

			try {
				this._commandDispatcher.dispatch(this, rawCommand);
			} catch (CommandNotFoundException e) {
				this._outputPrintStream.println("Command Not Found : [" + rawCommand + "]");
			} catch (RuntimeException e) {
				e.printStackTrace();
				break;
			}
		}
	}

	@Override
	public void stop() {
		this._stop = true;
		this._inputBufferedReader = null;
	}

	@Override
	public RemoteDirectory getRemoteRootDirectory() {
		return this._remoteRootDirectory;
	}

	@Override
	public RemoteDirectory getRemoteWorkingDirectory() {
		return this._remoteWorkingDirectory;
	}

	@Override
	public void setRemoteWorkingDirectory(RemoteDirectory newRemoteWorkingDirectory) {
		this._remoteWorkingDirectory = newRemoteWorkingDirectory;
	}

	@Override
	public java.io.File getLocalRootDirectory() {
		return this._localRootDirectory;
	}

	@Override
	public java.io.File getLocalWorkingDirectory() {
		return this._localWorkingDirectory;
	}

	@Override
	public void setLocalWorkingDirectory(java.io.File newLocalWorkingDirectory) {
		this._localWorkingDirectory = newLocalWorkingDirectory;
	}

	@Override
	public DocsService getDocsService() {
		return this._client;
	}

	@Override
	public OutputStream getOutputStream() {
		return this._outputStream;
	}
}
